package util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Map;

/**
 * SM2 解密工具类，用于对使用 SM2 算法加密的数据进行解密操作。
 */
public class SM2DecryptionUtil {

    // 静态代码块，在类加载时执行，将 Bouncy Castle 安全提供者添加到 Java 安全系统中
    // Bouncy Castle 提供了 SM2 算法的具体实现，添加该提供者后才能使用相关功能
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 使用 SM2 算法对加密文本进行解密。
     *
     * @param privateKeyBase64 经过 Base64 编码的 SM2 私钥
     * @param xmlEncryptionData    经过 Base64 编码的加密文本
     * @return 解密后的明文，以 UTF-8 编码的字符串形式返回
     * @throws Exception 当解密过程中出现异常，如密钥解析错误、解密操作失败等，抛出该异常
     */
    public static String decrypt(String privateKeyBase64, String xmlEncryptionData) throws Exception {
        // 将 Base64 编码的私钥字符串解码为字节数组
        byte[] privateKeyBytes = Base64.decode(privateKeyBase64);

        // 获取一个椭圆曲线（EC）密钥工厂实例，使用 Bouncy Castle 作为提供者
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");

        // 创建一个 PKCS8 编码的密钥规范对象，用于将私钥字节数组转换为 Java 私钥对象
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        // 根据密钥规范生成 Java 私钥对象
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        // 获取 SM2 算法使用的椭圆曲线参数，"sm2p256v1" 是 SM2 算法指定的曲线名称
        X9ECParameters ecParameters = GMNamedCurves.getByName("sm2p256v1");
        // 创建椭圆曲线域参数对象，包含曲线、基点、阶数和余因子等信息
        ECDomainParameters domainParameters = new ECDomainParameters(
                ecParameters.getCurve(),
                ecParameters.getG(),
                ecParameters.getN(),
                ecParameters.getH());

        // 从 Java 私钥对象中获取私钥的参数
        BigInteger d = ((java.security.interfaces.ECPrivateKey) privateKey).getS();
        // 使用私钥参数 S 和椭圆曲线域参数创建 Bouncy Castle 的椭圆曲线私钥参数对象
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(d, domainParameters);

        // 创建 SM2 加密引擎实例，用于执行解密操作
        SM2Engine engine = new SM2Engine();
        // 初始化加密引擎，设置为解密模式，传入私钥参数
        engine.init(false, privateKeyParameters);

        // 解码 Base64 编码的加密文本
        // 将 Base64 编码的加密文本字符串解码为字节数组
        byte[] encryptedBytes = Base64.decode(xmlEncryptionData);
        // 执行解密操作
        // 使用解密引擎对加密字节数组进行解密操作，得到解密后的字节数组
        byte[] decryptedBytes = engine.processBlock(encryptedBytes, 0, encryptedBytes.length);
        // 获取解密后的Base64 XML
        String base64Xml = new String(decryptedBytes, StandardCharsets.UTF_8);

        //解码Base64 XML
        byte[] xmlBytes = java.util.Base64.getDecoder().decode(base64Xml);
        String xmlData = new String(xmlBytes, StandardCharsets.UTF_8);
        // 将XML转换回JSON格式
        XmlMapper xmlMapper = new XmlMapper();
        ObjectMapper jsonMapper = new ObjectMapper();
        // 配置ObjectMapper确保日期格式正确
        jsonMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        // 先将XML反序列化为Map结构
        Map<String, Object> xmlMap = xmlMapper.readValue(xmlData, new TypeReference<Map<String, Object>>() {});
        // 再序列化为格式化的JSON字符串
        return jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(xmlMap);

    }
}
